import React, { Component } from "react";
import { Button } from "antd";

import ElevatorBody from "./components/ElevatorBody";
import ElevatorDoor from "./components/ElevatorDoor";
import ElevatorPanel from "./components/ElevatorPanel";
import Passager from "./components/Passager";

import {
  GetFloorNumArr,
  GetRandomOneToMaxF,
  GetRandomMoreOneF,
  DeepCloneMap,
  DelRepeat,
  Map2Array,
  Array2Map,
  SortByDirection,
} from "./Tools";
import { DIRECTION, maxWeight, weight, rateSrcNot1Des1, itvlEveryPass } from "./define";

import "./style/buildingUi.css";

class BuildingUI extends Component {
  upFloorArr = [];
  downFloorArr = [];
  isFullWeight = false;
  state = {
    scheduleIntervalID: 0, // 调度系统计时器ID
    autoStoryIvtID: 0, // 自动生成需求计算器ID
    elevDetailArr: [], // 电梯盒信息数组

    // 所有楼层——上行&下行 预备队列 Map（Key: 楼层数 Value: ArrsUpDown）
    everyFloorDesMap: Array2Map(GetFloorNumArr().map((v) => [v, { [DIRECTION.UP]: [], [DIRECTION.DOWN]: [] }])),
    asDesCountMap: Array2Map(GetFloorNumArr().map((v) => [v, 0])),
  };
  componentDidMount() {
    this.UpdateElevDetailArr();
    this.ScheduleElevator();
  }
  // ————————————————上下行队列相关begin————————————————
  GetQueneArr = (direction) => {
    switch (direction) {
      case DIRECTION.UP:
        return this.upFloorArr.slice();
      case DIRECTION.DOWN:
        return this.downFloorArr.slice();
      case DIRECTION.NONE:
        return [];
      default:
        break;
    }
    return [];
  };
  SetQueneArr = (direction, newQuene) => {
    if (typeof newQuene !== "object") {
      return;
    }
    SortByDirection(newQuene, direction);
    switch (direction) {
      case DIRECTION.UP:
        this.upFloorArr = newQuene;
        break;
      case DIRECTION.DOWN:
        this.downFloorArr = newQuene;
        break;
      default:
        break;
    }
  };
  // [上下行队列]更新某次需求带来的新上下行队列
  UpdateUpDownArr = (srcFloor, desFloor) => {
    const { elevDetailArr } = this.state;
    const newUpQueneArr = this.GetQueneArr(DIRECTION.UP);
    const newDownQueneArr = this.GetQueneArr(DIRECTION.DOWN);
    const currElevFloor = elevDetailArr[0].position;

    const UpdateArr = (direction, newQueneArr) => {
      newQueneArr.push(srcFloor);
      this.SetQueneArr(direction, DelRepeat(newQueneArr));
    };

    switch (elevDetailArr[0].direction) {
      // 1. 电梯不动
      case DIRECTION.NONE:
        // 1.1 需求<当前 下
        if (srcFloor < currElevFloor) {
          UpdateArr(DIRECTION.DOWN, newDownQueneArr);
        }
        // 1.2 需求>当前 上
        if (srcFloor > currElevFloor) {
          UpdateArr(DIRECTION.UP, newUpQueneArr);
        }
        // 1.3 需求=当前
        if (srcFloor === currElevFloor) {
          if (srcFloor < desFloor) {
            // 1.3.1 源<目的 上
            UpdateArr(DIRECTION.UP, newUpQueneArr);
          } else {
            // 1.3.2 源>目的 下
            UpdateArr(DIRECTION.DOWN, newDownQueneArr);
          }
        }
        break;
      // 2. 电梯上
      case DIRECTION.UP:
        // bug: 6->1, 10->1 电梯到达6楼后方向错乱
        // 2.1 源<目的
        if (srcFloor < desFloor) {
          // 2.1.1 当前>源 nothing
          if (srcFloor < currElevFloor) {
            break;
          }
          // 2.1.2 当前<=源 上
          if (!this.isFullWeight) {
            UpdateArr(DIRECTION.UP, newUpQueneArr); // 向上顺风车
          }
        } else {
          // 2.2 源>目的 下
          UpdateArr(DIRECTION.DOWN, newDownQueneArr);
        }
        break;
      // 3. 电梯下
      case DIRECTION.DOWN:
        // 3.1 源>目的
        if (srcFloor > desFloor) {
          // 3.1.1 当前<源 nothing
          if (srcFloor > currElevFloor) {
            break;
          }
          // 3.1.2 当前>=源 下 - 向下顺风车
          if (!this.isFullWeight) {
            UpdateArr(DIRECTION.DOWN, newDownQueneArr);
          }
        } else {
          // 3.2 源<目的 上
          UpdateArr(DIRECTION.UP, newUpQueneArr);
        }
        break;
      default:
        break;
    }
  };
  // ————————————————上下行队列相关end————————————————

  // ————————————————电梯盒相关begin————————————————
  // [elevDetail]更新所有电梯属性数据
  UpdateElevDetailArr = () => {
    this.setState({
      elevDetailArr: this.GetElevDetailArr(),
    });
  };
  // [elevDetail]实时获取所有电梯属性TODO：待完善
  GetElevDetailArr = (index = -1) => {
    if (index === -1) {
      // 返回全部
      if (this.eleBodyRef && this.eleBodyRef.state) {
        const { direction, position, currentWeight, showDirection } = this.eleBodyRef.state;
        return [
          {
            direction,
            position,
            currentWeight,
            floor: this.eleBodyRef.getFloor(),
            showDirection,
          },
        ];
      }
    }
    // 返回特定
    return [];
  };
  // ————————————————电梯盒相关end————————————————

  // ————————————————DesMap相关begin————————————————
  // 获取某楼层某方向的目的楼层
  GetElevPanelDesFloor = (floorNum, direction) => {
    switch (direction) {
      case DIRECTION.UP:
      case DIRECTION.DOWN:
        return this.state.everyFloorDesMap.get(floorNum)[direction];
      default:
        return [];
    }
  };
  // [DesMap]根据上下行需求更新DesMap[楼层, 目的楼层]
  UpdateFloorDesMap(floorNum, desFloor) {
    const newMap = DeepCloneMap(this.state.everyFloorDesMap);
    const ArrsUpDown = newMap.get(floorNum); // 获取楼层数对应的对象
    // 根据上下行方向为对应方向队列添加目的楼层
    if (floorNum < desFloor) {
      ArrsUpDown[DIRECTION.UP].push(desFloor);
    } else {
      ArrsUpDown[DIRECTION.DOWN].push(desFloor);
    }
    this.setState({
      everyFloorDesMap: newMap,
    });
  }
  // 清除某楼层某方向的目的楼层的特定人数；返回被清除的目的楼层（上电梯的乘客）
  ClearPanelDesFloor = (floorNum, direction, clearNum) => {
    const newMap = DeepCloneMap(this.state.everyFloorDesMap);
    const getInArr = newMap.get(floorNum)[direction].splice(0, clearNum);
    this.setState({
      everyFloorDesMap: newMap,
    });
    return getInArr;
  };
  // ————————————————DesMap相关end————————————————

  // ————————————————调度系统begin————————————————
  // [调度系统]-更新盒运动状态
  UpdateElevBodyDirection = () => {
    const { elevDetailArr } = this.state;
    if (elevDetailArr.length === 0) {
      return DIRECTION.ERROR;
    }
    const isUpEmpty = this.GetQueneArr(DIRECTION.UP).length === 0;
    const isDownEmpty = this.GetQueneArr(DIRECTION.DOWN).length === 0;
    let nextDirection = elevDetailArr[0].direction;
    switch (elevDetailArr[0].direction) {
      case DIRECTION.UP:
        if (!isUpEmpty) {
        } else if (!isDownEmpty) {
          nextDirection = DIRECTION.DOWN;
          this.HandleWork(nextDirection); // 方向发生变化
        } else {
          nextDirection = DIRECTION.NONE;
        }
        break;
      case DIRECTION.DOWN:
        if (!isDownEmpty) {
        } else if (!isUpEmpty) {
          nextDirection = DIRECTION.UP;
          this.HandleWork(nextDirection); // 方向发生变化
        } else {
          nextDirection = DIRECTION.NONE;
        }
        break;
      case DIRECTION.NONE:
        if (!isUpEmpty) {
          nextDirection = DIRECTION.UP;
        } else if (!isDownEmpty) {
          nextDirection = DIRECTION.DOWN;
        } else {
          this.HandleWork(DIRECTION.NONE); // 检查是否有因超重未完成的需求
        }
        break;
      default:
        break;
    }
    this.eleBodyRef.SetDirection(nextDirection, this.UpdateElevDetailArr);
    return nextDirection;
  };
  // 电梯运动方向发生变化或静止，处理接下来方向上的需求
  HandleWork = (nowDirection) => {
    if (this.eleBodyRef) {
      if (nowDirection === DIRECTION.NONE) {
        this.PollAsDesMap();
      } else {
        this.OnDirectionChange(nowDirection);
      }
    }
  };
  // 电梯待机状态轮询列表
  PollAsDesMap = () => {
    const { elevDetailArr } = this.state;
    const currElevFloor = elevDetailArr[0].position;
    // 收集向上的从下往上收集
    let hasUp = false;
    GetFloorNumArr().forEach((f) => {
      if (f === currElevFloor) {
        return;
      }
      const asDesArr = this.GetElevPanelDesFloor(f, DIRECTION.UP);
      if (asDesArr.length > 0 && !hasUp) {
        DelRepeat(asDesArr).forEach((desF) => {
          this.UpdateUpDownArr(f, desF);
        });
        hasUp = true;
      }
    });

    let hasDown = false;
    GetFloorNumArr()
      // 收集向下的从上往下收集
      .reverse()
      .forEach((f) => {
        if (f === currElevFloor) {
          return;
        }
        const asDesArr = this.GetElevPanelDesFloor(f, DIRECTION.DOWN);
        if (asDesArr.length > 0 && !hasDown) {
          DelRepeat(asDesArr).forEach((desF) => {
            this.UpdateUpDownArr(f, desF);
          });
          hasDown = true;
        }
      });
  };
  // 方向转变
  OnDirectionChange = (nowDirection) => {
    const { elevDetailArr } = this.state;
    const currElevFloor = elevDetailArr[0].position;
    GetFloorNumArr().forEach((f) => {
      if (f === currElevFloor) {
        return;
      }
      const asDesArr = this.GetElevPanelDesFloor(f, nowDirection);
      if (asDesArr.length > 0) {
        DelRepeat(asDesArr).forEach((desF) => {
          this.UpdateUpDownArr(f, desF);
        });
      }
    });
  };
  // [调度系统]-根据传入方向向最新楼层位移
  RerouteElev = (nowDirection) => {
    const { elevDetailArr } = this.state;
    if (elevDetailArr.length === 0) {
      return;
    }
    const upFloorArr = this.GetQueneArr(DIRECTION.UP);
    const downFloorArr = this.GetQueneArr(DIRECTION.DOWN);
    let currDesFloor = elevDetailArr[0].floor; // 当前目的楼层
    switch (nowDirection) {
      case DIRECTION.UP:
        currDesFloor = upFloorArr[0];
        break;
      case DIRECTION.DOWN:
        currDesFloor = downFloorArr[0];
        break;
      case DIRECTION.NONE:
      default:
        return;
    }
    // 到达特定楼层回调
    const OnArrived = () => {
      // 根据方向获取所需数据
      const upFloorArr = this.GetQueneArr(DIRECTION.UP);
      const downFloorArr = this.GetQueneArr(DIRECTION.DOWN);
      let currDesFloor = elevDetailArr[0].floor; // 当前目的楼层
      let newFloorArr = []; // 当前运动方向新队列
      let oppositeDirection = DIRECTION.NONE; // 反方向
      let oppositeDirectionArr = []; // 反方向数组
      switch (nowDirection) {
        case DIRECTION.UP:
          currDesFloor = upFloorArr[0];
          newFloorArr = upFloorArr;
          oppositeDirection = DIRECTION.DOWN;
          oppositeDirectionArr = downFloorArr.slice();
          break;
        case DIRECTION.DOWN:
          currDesFloor = downFloorArr[0];
          newFloorArr = downFloorArr;
          oppositeDirection = DIRECTION.UP;
          oppositeDirectionArr = upFloorArr.slice();
          break;
        case DIRECTION.NONE:
        default:
          return;
      }
      // 不管上不上乘客，都弹出当前楼层 1. 上完所有乘客，正常 2. 没上完所有乘客，正常 3. 没停，下一轮解决此需求
      newFloorArr.shift();
      // 到达后，1. 更新目的需求Count
      // 1.1记录不同需求楼层的人数
      const newMap = DeepCloneMap(this.state.asDesCountMap);
      // 1.2 减重，然后清零想到当前楼层人数
      let changeWeight = newMap.get(currDesFloor) * -weight;
      newMap.set(currDesFloor, 0);

      // 2 处理是否有人能上电梯的逻辑：判断当前重量是否已达到最大值
      const currentWeight = this.eleBodyRef.GetWeight();
      // 2.1 可以上人，加入当前楼层对应方向上的目的楼层
      if (currentWeight + changeWeight + weight <= maxWeight) {
        // 算下能上几个
        const restWeight = maxWeight - (currentWeight + changeWeight);
        const getInCount = parseInt(restWeight / weight);
        // 决定上谁
        const getInPassagerArr = this.ClearPanelDesFloor(currDesFloor, nowDirection, getInCount);

        getInPassagerArr.forEach((targetFloor) => {
          const targetFloorPassagerCount = newMap.get(targetFloor); // 获取楼层数对应的欲到达乘客数
          newMap.set(targetFloor, targetFloorPassagerCount + 1);
        });

        newFloorArr = DelRepeat([...newFloorArr, ...getInPassagerArr]); // 合并后去重
        changeWeight += getInPassagerArr.length * weight;
        this.eleBodyRef.AddWeight(changeWeight);
      }
      this.setState({
        asDesCountMap: newMap,
      });

      // 3 更新当前运动方向的数组
      // 3.1 如果已满载，直接将当前方向队列更新为目的数组方向
      // 计算乘客更新后，电梯剩下的重量
      const restWeight = maxWeight - (currentWeight + changeWeight);
      if (restWeight < weight) {
        const desArr = [];
        Map2Array(newMap).forEach(([f, c]) => {
          c > 0 && desArr.push(f);
        });
        this.isFullWeight = true;
        this.SetQueneArr(nowDirection, desArr);
      } else {
        // 3.2 未满载则正常处理
        this.isFullWeight = false;
        this.SetQueneArr(nowDirection, newFloorArr);
      }

      // 4 如果另外一个方向有需求，需要再返回这层楼一次
      if (this.GetElevPanelDesFloor(currDesFloor, oppositeDirection).length > 0) {
        oppositeDirectionArr = DelRepeat([currDesFloor, ...oppositeDirectionArr]);
      }
      this.SetQueneArr(oppositeDirection, oppositeDirectionArr);
    };
    // 此处要决定新的currDesFloor
    this.eleBodyRef.RunTo(currDesFloor, OnArrived);
  };
  // 调度系统Core逻辑
  ScheduleElevator() {
    // 调度系统本质上是一个定时器，轮询所有楼层的需求并维护电梯运动状态
    const localID = setInterval(() => {
      // 0. 清理旧调度系统
      clearInterval(this.state.scheduleIntervalID);
      // 1. 更新电梯盒的运动方向状态
      const nowDirection = this.UpdateElevBodyDirection();
      // 2. 向当前方向数组[0]楼层位移
      this.RerouteElev(nowDirection);
    }, 1200);
    this.setState({
      scheduleIntervalID: localID,
    });
  }
  // ————————————————调度系统end————————————————

  // ————————————————外界输入begin————————————————
  // 根据某个乘客需求调度电梯
  NewLaunch = (srcFloor, desFloor) => {
    // 更新上下队列
    this.UpdateUpDownArr(srcFloor, desFloor);
    // 更新src楼层的目的楼层
    this.UpdateFloorDesMap(srcFloor, desFloor);
  };
  RandomStory = () => {
    const srcFloor = GetRandomMoreOneF();
    let desFloor = GetRandomOneToMaxF();
    if (srcFloor !== 1 && Math.random() < rateSrcNot1Des1) {
      desFloor = 1;
    }
    while (desFloor === srcFloor) {
      desFloor = GetRandomOneToMaxF();
    }
    this.NewLaunch(srcFloor, desFloor);
  };
  // 自动生成乘梯需求
  AutoComposeStory = () => {
    const { autoStoryIvtID } = this.state;
    let localID;
    if (autoStoryIvtID !== 0) {
      clearInterval(autoStoryIvtID);
      localID = 0;
    } else {
      localID = setInterval(this.RandomStory, itvlEveryPass);
    }
    this.setState({
      autoStoryIvtID: localID,
    });
  };
  TestFn = () => {
    this.NewLaunch(1, 5);
    for (let i = 0; i < 15; i++) {
      this.NewLaunch(5, 1);
    }
    this.NewLaunch(3, 1);
  };
  // ————————————————外界输入end————————————————

  render() {
    const { elevDetailArr, asDesCountMap } = this.state;
    // const floorArr = [5, 10];
    // const everyFloorDesArr = Map2Array(everyFloorDesMap);
    return (
      <div className="building-ui-root">
        <div className="building-body">
          <div className="elev-shaft">
            <div id="elev-box" />
          </div>
          {GetFloorNumArr()
            .reverse()
            .map((floorNum) => {
              const thisFloorDesUpArr = this.GetElevPanelDesFloor(floorNum, DIRECTION.UP);
              const thisFloorDesDownArr = this.GetElevPanelDesFloor(floorNum, DIRECTION.DOWN);
              return (
                <div className="floor" key={floorNum}>
                  <div className="floor-info">
                    <div className="floor-num">{floorNum}F</div>
                    {asDesCountMap.get(floorNum) !== 0 && <div className="as-des">{asDesCountMap.get(floorNum)}人</div>}
                  </div>
                  <div className="elevator-module">
                    <ElevatorDoor elevDetailArr={elevDetailArr} currentFloor={floorNum} />
                    <ElevatorPanel
                      onLaunch={this.NewLaunch}
                      // currentFloorArray={floorArr}
                      elevDetailArr={elevDetailArr}
                      currentFloor={floorNum}
                    />
                    {/* <ElevatorDoor elevatorPosFloor={floorArr[1]} currentFloor={floorNum} /> */}
                  </div>
                  <div className="passagers">
                    {thisFloorDesUpArr.length > 0 && (
                      <div>
                        ↑&nbsp;
                        {thisFloorDesUpArr.map((f, i) => (
                          <Passager key={i} desFloor={f} />
                        ))}
                      </div>
                    )}
                    {thisFloorDesDownArr.length > 0 && (
                      <div>
                        ↓&nbsp;
                        {thisFloorDesDownArr.map((f, i) => (
                          <Passager key={i} desFloor={f} />
                        ))}
                      </div>
                    )}
                  </div>
                </div>
              );
            })}
        </div>
        <div>
          <ElevatorBody onRef={(r) => (this.eleBodyRef = r)} onMove={() => this.UpdateElevDetailArr()} />
          <div className="data-show">
            电梯运动方向:
            {this.eleBodyRef &&
              ((this.eleBodyRef.state.direction === 1 && "上") || (this.eleBodyRef.state.direction === 2 && "下"))}
            <div>&nbsp;</div>
            <div>
              <Button type="primary" onClick={this.AutoComposeStory}>
                自动生成需求
              </Button>
            </div>
            <Button type="primary" onClick={this.RandomStory}>
              生成随机乘梯需求
            </Button>
            <Button onClick={this.TestFn}>测试按钮</Button>
            <div>向上队列[{this.GetQueneArr(DIRECTION.UP).join("->")}]</div>
            <div>向下队列[{this.GetQueneArr(DIRECTION.DOWN).join("->")}]</div>
            {/* <div>&nbsp;</div>
            {everyFloorDesArr.map(
              ([f, des]) =>
                des[DIRECTION.UP].length + des[DIRECTION.DOWN].length > 0 && (
                  <div key={f}>
                    {f}楼:[上行:[{des[DIRECTION.UP].join(",")}],下行:[{des[DIRECTION.DOWN].join(",")}]]
                  </div>
                )
            )} */}
          </div>
        </div>
      </div>
    );
  }
}

export default BuildingUI;
